package eva

import (
	"fmt"
	"gitcode.com/deyiyangyang/gascheme/obj"
)

// cons函数 - 构造列表，将一个元素添加到另一个列表的开头
func Cons(args []obj.Object, env obj.Environment) obj.Object {
    if len(args) != 2 {
        return &obj.Error{Message: "cons requires exactly 2 arguments"}
    }

    // If the second argument is a list, prepend the first argument to it (flatten)
    if list, ok := args[1].(*obj.List); ok {
        // Create a new list with the first element prepended to the existing list
        if list.Car == nil && list.Cdr == nil {
            // Empty list case - create a list with just the first element
            return &obj.List{Car: args[0], Cdr: &obj.List{}}
        }
        
        // Non-empty list case - prepend first argument to the existing list
        return &obj.List{Car: args[0], Cdr: list}
    }

    // If the second argument is not a list, create a dotted pair
    return &obj.List{Car: args[0], Cdr: args[1]}
}

// car函数 - 返回列表的第一个元素
func Car(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 1 {
		return &obj.Error{Message: "car requires exactly 1 argument"}
	}

	list, ok := args[0].(*obj.List)
	if !ok {
		return &obj.Error{Message: "car argument must be a list"}
	}

	// Check for empty list
	if list.Car == nil {
		return &obj.Error{Message: "car: cannot apply to empty list"}
	}

	return list.Car
}

// cdr函数 - 返回列表的其余元素
func Cdr(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 1 {
		return &obj.Error{Message: "cdr requires exactly 1 argument"}
	}

	list, ok := args[0].(*obj.List)
	if !ok {
		return &obj.Error{Message: "cdr argument must be a list"}
	}

	// Check for empty list
	if list.Car == nil {
		return &obj.Error{Message: "cdr: cannot apply to empty list"}
	}

	return list.Cdr
}

// list函数 - 构造一个包含任意数量元素的列表
func List(args []obj.Object, env obj.Environment) obj.Object {
    if len(args) == 0 {
        // Empty list
        return &obj.List{}
    }
    
    // Build the list from right to left
    // The last element should point to an empty list
    result := &obj.List{Car: args[len(args)-1], Cdr: &obj.List{}}
    
    // Build the rest of the list from right to left
    for i := len(args) - 2; i >= 0; i-- {
        result = &obj.List{Car: args[i], Cdr: result}
    }
    
    return result
}

// null?函数 - 判断一个对象是否为空列表
func Null(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 1 {
		return &obj.Error{Message: "null? requires exactly 1 argument"}
	}

	list, ok := args[0].(*obj.List)
	if !ok {
		return &obj.Boolean{Value: false}
	}

	// Empty list has both Car and Cdr as nil
	return &obj.Boolean{Value: list.Car == nil && list.Cdr == nil}
}

// pair?函数 - 判断一个对象是否为序对
func Pair(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 1 {
		return &obj.Error{Message: "pair? requires exactly 1 argument"}
	}

	list, ok := args[0].(*obj.List)
	if !ok {
		return &obj.Boolean{Value: false}
	}

	return &obj.Boolean{Value: list.Length() > 0}
}

// Display function - outputs its argument to stdout without a newline
func Display(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 1 {
		return &obj.Error{Message: "display requires exactly 1 argument"}
	}

	// For strings, output the value without quotes
	if str, ok := args[0].(*obj.String); ok {
		fmt.Print(str.Value)
	} else {
		// For other objects, output their string representation
		fmt.Print(args[0].Inspect())
	}

	// In Scheme, display returns an unspecified value that should not be printed
	return &obj.SilentVoid{}
}

// Newline function - outputs a newline character to stdout
func Newline(args []obj.Object, env obj.Environment) obj.Object {
	if len(args) != 0 {
		return &obj.Error{Message: "newline requires no arguments"}
	}

	fmt.Println()

	// In Scheme, newline returns an unspecified value that should not be printed
	return &obj.SilentVoid{}
}

// RegisterListProcedures 注册所有列表操作内置过程
func RegisterListProcedures(globalEnv *obj.GlobalEnvironment) {
	globalEnv.Set("cons", &obj.Builtin{Fn: Cons})
	globalEnv.Set("car", &obj.Builtin{Fn: Car})
	globalEnv.Set("cdr", &obj.Builtin{Fn: Cdr})
	globalEnv.Set("list", &obj.Builtin{Fn: List})
	globalEnv.Set("null?", &obj.Builtin{Fn: Null})
	globalEnv.Set("pair?", &obj.Builtin{Fn: Pair})
	globalEnv.Set("display", &obj.Builtin{Fn: Display})
	globalEnv.Set("newline", &obj.Builtin{Fn: Newline})
}
